
// GenCovReportDlg.cpp : implementation file
//

#include "stdafx.h"
#include "GenCovReport.h"
#include "GenCovReportDlg.h"
#include "interface.h"
#include "FolderDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

CGenCovReportDlg* g_dlg = NULL;

// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	enum { IDD = IDD_ABOUTBOX };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

// Implementation
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()


// CGenCovReportDlg dialog




CGenCovReportDlg::CGenCovReportDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CGenCovReportDlg::IDD, pParent)
	, m_iFilterType(1)
	, m_sFilter(_T(""))
	, m_pLines(NULL)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CGenCovReportDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_SOURCELIST, m_lstSources);
	DDX_Control(pDX, IDC_FILTERLIST, m_lstFilters);
	DDX_Control(pDX, IDC_LISTRESULTS, m_lstFiles);
	DDX_Radio(pDX, IDC_EXCLUDE, m_iFilterType);
	DDX_Text(pDX, IDC_TXTFILTERS, m_sFilter);
}

BEGIN_MESSAGE_MAP(CGenCovReportDlg, CDialog)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_BN_CLICKED(IDC_SELECTCOV, &CGenCovReportDlg::OnBnClickedSelectcov)
	ON_BN_CLICKED(IDC_DELFILE, &CGenCovReportDlg::OnBnClickedDelfile)
	ON_BN_CLICKED(IDC_ADDFILTER, &CGenCovReportDlg::OnBnClickedAddfilter)
	ON_BN_CLICKED(IDC_DELFILTER, &CGenCovReportDlg::OnBnClickedDelfilter)
	ON_BN_CLICKED(IDC_GENERATE, &CGenCovReportDlg::OnBnClickedGenerate)
	ON_BN_CLICKED(IDC_APPLYFILTER, &CGenCovReportDlg::OnBnClickedApplyfilter)
END_MESSAGE_MAP()


// CGenCovReportDlg message handlers

BOOL CGenCovReportDlg::OnInitDialog()
{
	g_dlg = this;
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	// TODO: Add extra initialization here

	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CGenCovReportDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CGenCovReportDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CGenCovReportDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}


void CGenCovReportDlg::OnBnClickedSelectcov()
{
	CFileDialog	fileDialog(TRUE, _T(".xml"), NULL, OFN_OVERWRITEPROMPT, _T("VS2008 Coverage Reports (*.xml)|*.xml||"));
	if(fileDialog.DoModal() == IDOK)
	{
		CString	szFile = fileDialog.GetPathName();
		CString szFileName = fileDialog.GetFileName();
		int nIdx = m_lstSources.InsertString(m_lstSources.GetCount(), szFileName);
		TCHAR* pszFile = new TCHAR[szFile.GetLength() + 1];
		_tcscpy_s(pszFile, szFile.GetLength() + 1, szFile);
		m_lstSources.SetItemDataPtr(nIdx, pszFile);

		RefreshFiles();
	}
}

void CGenCovReportDlg::OnBnClickedDelfile()
{
	int nIdx = m_lstSources.GetCurSel();
	TCHAR* pszFile = (TCHAR*)m_lstSources.GetItemDataPtr(nIdx);
	m_lstSources.SetItemDataPtr(nIdx, NULL);
	m_lstSources.DeleteString(nIdx);
	delete [] pszFile;

	RefreshFiles();
}

BOOL CGenCovReportDlg::FileNameMatch(const wstring& sFile1, const wstring& sFile2)
{
	int nPos = sFile1.find_last_of(L'\\');
	if(nPos == sFile1.npos)
		return FALSE;
	wstring sFileName1 = sFile1.substr(nPos + 1);

	nPos = sFile2.find_last_of(L'\\');
	if(nPos == sFile2.npos)
		return FALSE;

	wstring sFileName2 = sFile2.substr(nPos + 1);
	return sFileName1 == sFileName2;
}

void CGenCovReportDlg::RefreshFiles()
{
	ClearFiles();

	//Enumerate all the source files
	int nCnt = m_lstSources.GetCount();
	int i = 0;
	for(i = 0; i < nCnt; i++)
	{
		TCHAR* pszFile = (TCHAR*)m_lstSources.GetItemDataPtr(i);
		FILELIST lstFiles;
		m_Parser.Open(pszFile);
		m_Parser.ListFiles(eRuleExclude, WSTRINGLIST(), lstFiles);
		FILELIST::iterator it = lstFiles.begin();
		for(; it != lstFiles.end(); it++)
		{
			if(!CheckFilter(it->first.c_str()))
				continue;

			int nFilesCount = m_lstFiles.GetCount();
			int j = 0;
			for(j = 0; j < nFilesCount; j++)
			{
				CFileProp *pProp = (CFileProp*)m_lstFiles.GetItemDataPtr(j);
				if(FileNameMatch(pProp->sFilePath, it->first))
				{
					pProp->lstFileID.push_back(make_pair(wstring(pszFile), it->second));
					break;
				}
			}
			if(j >= nFilesCount)
			{
				int nIdx = m_lstFiles.InsertString(m_lstFiles.GetCount(), it->first.c_str());
				CFileProp *pProp = new CFileProp;
				m_lstFiles.SetItemDataPtr(nIdx, pProp);
				pProp->lstFileID.push_back(make_pair(wstring(pszFile), it->second));
				pProp->sFilePath = it->first;
			}
		}
		m_Parser.Close();
	}
}

void CGenCovReportDlg::ClearFiles()
{
	int nCnt = m_lstFiles.GetCount();
	int i = 0;
	for(i = 0; i < nCnt; i++)
	{
		CFileProp* pProp = (CFileProp*)m_lstFiles.GetItemDataPtr(i);
		m_lstFiles.SetItemDataPtr(i, NULL);
		delete pProp;
	}
	m_lstFiles.ResetContent();
}

void CGenCovReportDlg::OnBnClickedAddfilter()
{
	UpdateData();
	m_lstFilters.InsertString(m_lstFilters.GetCount(), m_sFilter);
	m_sFilter.Empty();
	UpdateData(FALSE);
}

void CGenCovReportDlg::OnBnClickedDelfilter()
{
	m_lstFilters.DeleteString(m_lstFilters.GetCurSel());
}

BOOL CGenCovReportDlg::CheckFilter(LPCWSTR lpszFileName)
{
	CString sFilePath(lpszFileName);
	int nCount = m_lstFilters.GetCount();
	if(nCount == 0)
		return TRUE;

	for(int i = 0; i < nCount; i++)
	{
		CString sPropName;
		m_lstFilters.GetText(i, sPropName);
		if(sFilePath.Find(sPropName) >= 0)
		{
			if(m_iFilterType == 0)
				return FALSE;
			else
				return TRUE;
		}
	}
	if(m_iFilterType == 0)
		return TRUE;
	else
		return FALSE;
}

void CGenCovReportDlg::OnBnClickedGenerate()
{
	int nCnt = m_lstFiles.GetCount();
	if(nCnt == 0)
	{
		AfxMessageBox(_T("Please add coverage files firstly."));
		return;
	}

	CFolderDlg dlg;
	if(IDOK != dlg.DoModal(m_hWnd))
	{
		return;
	}
	if(!PathIsDirectory(dlg.m_sPath))
	{
		SHCreateDirectory(NULL, dlg.m_sPath);
	}

	CStringA sIdxOutFile(dlg.m_sPath);
	sIdxOutFile += "\\index.html";
	FILE* fpIdx = fopen(sIdxOutFile, "w");
	if(fpIdx == NULL)
		return;

	fprintf(fpIdx, "<html>\n<head>\n</head>\n<body>\n<table border='1'>\n<tr>\n<td width='40%'>File Name</td>");
	fprintf(fpIdx, "<td>Line Covered</td>\n	<td>Total Line</td>\n	<td>Line Coverage</td>\n	<td>Block Covered</td>\n	<td>Block UnCovered</td>\n	<td>Block Coverage</td>\n</tr>");

	int i = 0;
	for(i = 0; i < nCnt; i++)
	{
		CFileProp* pProp = (CFileProp*)m_lstFiles.GetItemDataPtr(i);
		if(pProp == NULL)
			continue;

		FILELIST::iterator it = pProp->lstFileID.begin();
		for(; it != pProp->lstFileID.end(); it++)
		{
			COVERAGE_DATA covData = {0};
			m_Parser.Open(it->first.c_str());
			m_Parser.GetFileCoverage(pProp->sFilePath.c_str(), it->second, pProp->lines, &covData);
			m_Parser.Close();
			if(covData.nLinesCovered > pProp->covData.nLinesCovered)
				pProp->covData = covData;
		}

		CStringA sFileName(pProp->sFilePath.c_str());
		int nPos = sFileName.ReverseFind('\\');
		CStringA sSourceName = sFileName.Right(sFileName.GetLength() - nPos);
		sSourceName += ".html";
		CStringA sOutFile(dlg.m_sPath);
		sOutFile += sSourceName;
		m_pLines = &pProp->lines;
		parseFile((LPCSTR)sFileName, (LPCSTR)sOutFile, TRUE);

		sSourceName = sSourceName.Mid(1);
		fprintf(fpIdx, "<tr>\n<td><a href='%s'>%s</a></td>\n", (LPCSTR)sSourceName, (LPCSTR)sSourceName);
		int nLinesCovered = GetLinesCovered();
		int nTotalLines = pProp->covData.nLinesNotCovered + pProp->covData.nLinesCovered + pProp->covData.nLinesPartiallyCovered;
		fprintf(fpIdx, "<td>%d</td>\n", nLinesCovered);
		fprintf(fpIdx, "<td>%d</td>\n", nTotalLines);
		fprintf(fpIdx, "<td>%.2f%%</td>\n", nLinesCovered * 100.0f / nTotalLines);
		fprintf(fpIdx, "<td>%d</td>\n", pProp->covData.nBlocksCovered);
		fprintf(fpIdx, "<td>%d</td>\n", pProp->covData.nBlocksNotCovered);
		fprintf(fpIdx, "<td>%.2f%%</td>\n</tr>\n", pProp->covData.nBlocksCovered * 100.0 / (pProp->covData.nBlocksCovered + pProp->covData.nBlocksNotCovered));

		m_pLines = NULL;
	}

	fprintf(fpIdx, "</table>\n</body>\n</html>\n");
	fclose(fpIdx);
}

void CGenCovReportDlg::OnBnClickedApplyfilter()
{
	RefreshFiles();
}

int CGenCovReportDlg::GetLinesCovered()
{
	int nCnt = 0;
	LINELIST::iterator it = m_pLines->begin();
	for(; it != m_pLines->end(); it++)
	{
		if(it->second > 0)
			nCnt++;
	}
	return nCnt;
}

int CGenCovReportDlg::IsLineCovered(int nLineNo)
{
	if(m_pLines != NULL)
	{
		LINELIST::iterator it = m_pLines->find(nLineNo);
		if(it != m_pLines->end())
		{
			return it->second;
		}
		return 0;
	}
	return 0;
}

extern "C" int IsLineCovered(int nLineNo)
{
	if(g_dlg != NULL)
		return g_dlg->IsLineCovered(nLineNo);
	return 0;
}
